# -*- coding: utf-8 -*-

import pika
import time
import json
import logging
import configparser
from daemon import DaemonContext
import callbacks  
import os
import sys
import multiprocessing

# 设置日志

workpath = '/www/wwwroot/crm_consumer'

def setup_logger(name, log_file, level=logging.INFO):
    """创建一个独立的日志记录器"""

    # 确保日志目录存在
    log_dir = 'logs'
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)
    handler = logging.FileHandler(log_file)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)

    logger = logging.getLogger(name)
    logger.setLevel(level)
    logger.addHandler(handler)
    
    return logger

# 创建独立的日志记录器
consumer_logger = setup_logger('consumer_logger', workpath+'/logs/consumer.log')
callback_logger = setup_logger('callback_logger', workpath+'/logs/callback.log')

def load_config():
    """加载RabbitMQ配置"""
    config = configparser.ConfigParser()
    config.read('config.ini')
    
    rabbitmq_config = {
        'host': config.get('rabbitmq', 'host'),
        'port': config.getint('rabbitmq', 'port'),
        'queue': config.get('rabbitmq', 'queue'),
        'exchange': config.get('rabbitmq', 'exchange'),
        'dlx_queue': config.get('rabbitmq', 'dlx_queue'),
        'username': config.get('rabbitmq', 'username'),
        'password': config.get('rabbitmq', 'password'),
        'dlx_exchange': config.get('rabbitmq', 'dlx_exchange'),
        'dlx_routing_key': config.get('rabbitmq', 'dlx_routing_key'),
        'vhost': config.get('rabbitmq', 'vhost', fallback='/'),  # 默认虚拟主机为 "/"
        'consumer_count': config.getint('rabbitmq', 'consumer_count', fallback=3)  # 默认消费者数量为3
    }
    return rabbitmq_config


def dispatch_callback(ch, method, properties, body):
    try:
        # 假设消息体是 JSON 格式，包含 "callback_type"
        import json
        message = json.loads(body.decode())
        callback_type = message.get("callback_type")
        # 动态获取回调函数，getattr 从 callbacks 模块中查找函数
        callback_function = getattr(callbacks, f"callback_{callback_type}", None)

        if callback_function is None:
            raise AttributeError(f"Callback function not found for type: {callback_type}")

        # 调用回调函数
        callback_function(ch, method, properties, body,consumer_logger)
        
    except AttributeError as e:
        # 记录错误日志
        consumer_logger.error(str(e))
        # 处理消息确认或拒绝等逻辑
        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
    except Exception as e:
        # 捕捉其他异常
        consumer_logger.error(f"Error processing message: {str(e)}")
        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)


def start_consumer():
    """
    启动 RabbitMQ 消费者
    """
    
    rabbitmq_config = load_config()  # 加载配置文件
    print(rabbitmq_config)
    while True:
        try:
            # 创建连接和通道
            credentials = pika.PlainCredentials(rabbitmq_config['username'], rabbitmq_config['password'])
            connection = pika.BlockingConnection(pika.ConnectionParameters(
                host=rabbitmq_config['host'],
                port=rabbitmq_config['port'],
                credentials=credentials,
                virtual_host=rabbitmq_config['vhost'],  # 添加 vhost

            ))
            channel = connection.channel()

            # 确保队列存在
            
            # channel.queue_delete(queue=rabbitmq_config['queue'])

            # 声明死信交换机
            channel.exchange_declare(exchange=rabbitmq_config['dlx_exchange'], exchange_type='direct',durable=True)

            # 声明死信队列
            channel.queue_declare(queue=rabbitmq_config['dlx_queue'], durable=True)

            # 将死信队列绑定到死信交换机
            channel.queue_bind(exchange=rabbitmq_config['dlx_exchange'], queue=rabbitmq_config['dlx_queue'],routing_key=rabbitmq_config['dlx_routing_key'])

            channel.exchange_declare(exchange=rabbitmq_config['exchange'],durable=True,exchange_type='direct')
            # 重新声明 normal_queue 队列，并绑定死信交换机
            args = {
                'x-dead-letter-exchange': rabbitmq_config['dlx_exchange'],
                'x-dead-letter-routing-key': rabbitmq_config['dlx_routing_key']
            }
            channel.queue_declare(queue=rabbitmq_config['queue'], arguments=args, durable=True)
            channel.queue_bind(rabbitmq_config['queue'],rabbitmq_config['exchange'])

            # 告诉 RabbitMQ 每次发送给消费者的消息数量
            channel.basic_qos(prefetch_count=1)

            # 开始消费，使用 dispatch_callback 进行消息分发
            channel.basic_consume(queue=rabbitmq_config['queue'], on_message_callback=dispatch_callback)

            consumer_logger.info('Waiting for messages. To exit press CTRL+C')
            channel.start_consuming()

        except pika.exceptions.AMQPConnectionError as e:
            # 捕捉 RabbitMQ 连接错误，日志记录并重试
            consumer_logger.error(f"Connection to RabbitMQ failed: {str(e)}. Retrying in 5 seconds...")
            time.sleep(5)
        except Exception as e:
            # 捕捉其他错误，日志记录并重试
            consumer_logger.error(f"Unexpected error: {str(e)}. Restarting consumer...")
            time.sleep(5)

def run_consumer():
    """
    运行单个消费者进程
    """
    try:
        start_consumer()
    except KeyboardInterrupt:
        consumer_logger.info("Consumer stopped manually")
    except Exception as e:
        consumer_logger.error(f"Fatal error in consumer: {str(e)}")

if __name__ == '__main__':
    # 使用 DaemonContext 创建守护进程
    with DaemonContext(
        working_directory=workpath,  # 设置工作目录，守护进程将从这里运行
        umask=0o002,               # 设置文件权限掩码
        stdout=open(workpath+'/logs/daemon_stdout.log', 'w+'),  # 重定向标准输出到日志文件
        stderr=open(workpath+'/logs/daemon_stderr.log', 'w+')   # 重定向标准错误到日志文件
    ):
        daemon_logger = setup_logger('daemon_logger', workpath+'/logs/daemon.log')

        try:
            daemon_logger.info("Starting consumer daemon")
            
            # 获取配置的消费者数量
            rabbitmq_config = load_config()
            consumer_count = rabbitmq_config['consumer_count']
            
            # 创建多个消费者进程
            processes = []
            for _ in range(consumer_count):
                process = multiprocessing.Process(target=run_consumer)
                processes.append(process)
                process.start()
            
            # 等待所有进程完成
            for process in processes:
                process.join()
                
        except KeyboardInterrupt:
            daemon_logger.info("Consumer daemon stopped manually")
        except Exception as e:
            daemon_logger.error(f"Fatal error: {str(e)}")
